package org.bhf.validation;

/**
 * Copyright (c) 2007, Blue Hole Software. All rights reserved.
 * Code licensed under the Apache 2.0 License:
 * http://www.apache.org/licenses/
 */

import org.bhf.i18n.Messages;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Public entry point for validating a POJO.
 */
public final class Validator
{
    final Messages      messages    = new Messages();
    final List<String>  context     = new ArrayList<String>( 16 );

    /**
     * Validate a general object, which could be an array, collection, or POJO.
     *
     * @param object May be <ocde>null</code>, in which case no validation will be done
     * @return A list of validation failure messages. If validate succeeds, the list will
     *      be empty, but not null.
     * @throws java.io.IOException Write error generating the validation helper class
     */
    public Messages     validateObject( final Object object )
        throws IOException
    {
        if( object != null )
        {
            final Method validator = getValidatorFor( object.getClass() );

            try
            {
                validator.invoke( null, object, this );
            }
            catch( IllegalAccessException e )
            {
                throw new RuntimeException( e );
            }
            catch( InvocationTargetException e )
            {
                throw new RuntimeException( e );
            }
        }

        return messages;
    }

    /**
     * Return the current messages.
     * @return The current messages.
     * @exclude
     */
    public Messages     getMessages()
    {
        return messages;
    }


    /**
     * Return the current context stack (a stack of properties and indices).
     * @return The current context stack.
     * @exclude
     */
    public List<String> getContext()
    {
        return context;
    }

    //
    // Private
    //

    // From type signature to validate method
    private static final Map<String,Method> validators = new HashMap<String,Method>();

    private Method  getValidatorFor( final Class clazz )
        throws IOException
    {
        final String typeSignature = Util.getDeclarationName( clazz, false );

        Method validator;

        synchronized( validators )
        {
            validator = validators.get( typeSignature );
            if( validator == null )
            {
                final Class<?> genClass = new ValidatorGenerator().generateFor( clazz );

                try
                {
                    validator = genClass.getMethod( "validate", clazz, Validator.class );
                }
                catch( final NoSuchMethodException nsme )
                {
                    throw new RuntimeException( nsme ); // Soften: internal error to not have method
                }
                validators.put( typeSignature, validator );
            }
        }

        return validator;
    }
}
